import random
from django import forms
from django.conf import settings
from django.core.validators import RegexValidator ## 正则表达式使用的模块
from django.core.exceptions import ValidationError ## 正则表达式校验错误的模块
from utils.tencent import sms
from utils.encrypt import md5
from utils.BootStrapForm import BootStrapForm
from web import models
from django_redis import get_redis_connection # 导入模块django_redis
# 创建注册的modelform
class RegisterModelForm(BootStrapForm,forms.ModelForm):
    mobile_phone = forms.CharField(
        label='手机号码',
        validators=[RegexValidator(r'^(1[3|4|5|6|7|8|9])\d{9}$','手机号格式错误')]
        ) ## 重写手机号码字段，在它生成的输入框中输入数据时会进行validators=[]正则表达式的校验
    password = forms.CharField(
        label='密码',
        min_length=8,
        max_length=64,
        error_messages={
            'min_length': '密码长度不能小于8位',
            'max_length': '密码长度不能大于64位',
        },
        widget=forms.PasswordInput()) ## 利用插件将密码框改成密码类型的输入框，输入数据时将显示...或者***
    # 利用参数attrs重写样式。当用下面的__init__方法重写样式时，就不需要在这里添加了
    confirm_password = forms.CharField(
        label='确认密码',
        widget=forms.PasswordInput(attrs={'class':'form-control','placeholder':'确认密码'})
        )## 数据表中有这个字段将重写，没有这个字段将新增这个字段。
    code = forms.CharField(
        label='验证码',
        widget=forms.PasswordInput(attrs={'class': 'form-control', 'placeholder': '验证码'})
        )

    class Meta:
        model = models.UserInfo
        fields = ['username','email','password','confirm_password','mobile_phone','code']
    # ## 判断是否有样式，有的话就重写class和placeholder，没有的话就直接覆盖添加
    # def __init__(self,*args,**kwargs):
    #     super().__init__(*args,**kwargs)
    #     for name,field in self.fields.items():
    #         if field.widget:
    #             field.widget.attrs['class'] = 'form-control'
    #             field.widget.attrs['placeholder'] = field.label
    #         else:
    #             field.widget.attrs={'class': 'form-control', 'placeholder': field.label}
    def clean_username(self):
        username = self.cleaned_data.get('username')  ## 验证通过的(上面的验证即手机号码错误的验证)才能让钩子函数获取到，否则获取的是null
        exists = models.UserInfo.objects.filter(username=username).exists() ## 在数据库中查询获取的手机号是否存在记录，返回的结果为bool值
        if exists:
            raise ValidationError('用户名已被注册')
        return username
    # 加密密码。调用写好的加密函数，把密码传入到函数中，通过加密后保存到数据库的表中
    def clean_password(self):
        pwd = md5(self.cleaned_data.get('password'))
        return pwd
    def clean_confirm_password(self):
        pwd = self.cleaned_data.get('password')
        confirm_pwd = md5(self.cleaned_data['confirm_password'])
        if pwd != confirm_pwd:
            raise ValidationError('密码不一致，请重新输入')
        return confirm_pwd
    def clean_mobile_phone(self):
        mobile_phone = self.cleaned_data.get('mobile_phone')  ## 验证通过的(上面的验证即手机号码错误的验证)才能让钩子函数获取到，否则获取的是null
        exists = models.UserInfo.objects.filter(mobile_phone=mobile_phone).exists() ## 在数据库中查询获取的手机号是否存在记录，返回的结果为bool值
        if exists:
            raise ValidationError('手机号码已被注册')
        return mobile_phone
    def clean_code(self):
        code = self.cleaned_data.get('code')
        mobile_phone = self.cleaned_data.get('mobile_phone')
        if mobile_phone is not None:
            conn = get_redis_connection() # 从res地址池中获取一个链接
            redis_code = conn.get(mobile_phone) # 获取res中储存的mobile_phone的值。当点击发送验证码按钮时，生成的随机验证码将会保存在redis中
            if not redis_code:
                raise ValidationError('验证码已失效')
            redis_str_code = redis_code.decode('utf-8') ## 把获取到的字节数据变成字符串
            if code.strip() != redis_str_code.strip():
                raise ValidationError('输入的验证码错误')
        return code


# 对通过后端推送过来的数据进行校验
class SendSmsForm(forms.Form):

    mobile_phone = forms.CharField(label='手机号',validators=[RegexValidator(r'^(1[3|4|5|6|7|8|9])\d{9}$','手机号格式错误')])
    ## 通过重写__init__()方法，让类SendSmsForm能多携带一个参数request过来，request就可以把tpl的数据携带过来，从而进行校验
    def __init__(self,request,*args,**kwargs):
        super().__init__(*args,**kwargs)
        self.request = request

    # 要想校验手机号码是否已经注册过，需要用到mobile_phone的钩子方法。钩子方法的cleaned_data中包含通过校验的所有的数据
    def clean_mobile_phone(self):
        mobile_phone = self.cleaned_data['mobile_phone']  ## 验证通过的(上面的验证即手机号码错误的验证)才能让钩子函数获取到，否则获取的是null
        # 获取前端推送过来的tpl,和settings.py中保存的tpl进行比较，用来判断tpl的数据是否有误
        tpl = self.request.GET.get('tpl')  ## 获取前端推送过来的tpl，前端页面register.html通过ajax推送过来的tpl数据：data: {mobile_phone: mobile_phone, tpl: "register"}
        template_id = settings.TENCENT_SMS_TEMPLATE.get(tpl) ## 获取settings.py中保存的tpl，获取的是tpl为register或者是login的模版信息
        if not template_id:
            raise ValidationError('模版错误')

        exists = models.UserInfo.objects.filter(mobile_phone=mobile_phone).exists()  ## 在数据库中查询获取的手机号是否存在记录，返回的结果为bool值
        if tpl == 'login':
            if not exists:
                raise ValidationError('手机号码未注册，请先注册')
        else:
            if exists:
                raise ValidationError('手机号码已被注册')
        # 发短信，并且写redis
        code = random.randrange(1000,9999)
        result = sms.send_sms_single(mobile_phone,template_id,[code,])
        if result['result'] != 0:
            raise ValidationError('短信发送失败，{}'.format(result['errmsg']))
        conn = get_redis_connection()  ## 利用django自带的模块，建立redis连接池，并设置连接池中的数据和超时的时间
        conn.set(mobile_phone,code,ex=60)
        return code   ## 返回的是什么数据，在用form.save()保存的时候数据库里将会保存什么数据。即这个返回的数据决定了字段mobile_phone的数值

class LoginSMSForm(BootStrapForm,forms.Form):
    """由于短信登陆不需要写入数据库，所以可以直接用Form组件"""
    mobile_phone = forms.CharField(
        label='手机号码',

        validators=[RegexValidator(r'^(1[3|4|5|6|7|8|9])\d{9}$','手机号格式错误')]
        ) ## 重写手机号码字段，在它生成的输入框中输入数据时会进行validators=[]正则表达式的校验
    code = forms.CharField(
        label='验证码',
        widget=forms.PasswordInput(attrs={'class': 'form-control', 'placeholder': '验证码'})
        )

    class Meta:
        model = models.UserInfo
        fields = ['mobile_phone','code']
    # ## 判断是否有样式，有的话就重写class和placeholder，没有的话就直接覆盖添加
    # def __init__(self,*args,**kwargs):
    #     super().__init__(*args,**kwargs)
    #     for name,field in self.fields.items():
    #         if field.widget:
    #             field.widget.attrs['class'] = 'form-control'
    #             field.widget.attrs['placeholder'] = field.label
    #         else:
    #             field.widget.attrs={'class': 'form-control', 'placeholder': field.label}
    def clean_mobile_phone(self):
        mobile_phone = self.cleaned_data.get('mobile_phone')
        user_object = models.UserInfo.objects.filter(mobile_phone=mobile_phone).first()  ## 在数据库中查询获取的手机号是否存在记录，返回的结果为bool值
        if not user_object:
            raise ValidationError('手机号码不存在')
        return user_object

    def clean_code(self):
        code = self.cleaned_data.get('code')
        user_object = self.cleaned_data.get('mobile_phone')
        if user_object is not None:
            conn = get_redis_connection()
            redis_code = conn.get(user_object.mobile_phone)
            if not redis_code:
                raise ValidationError('验证码已失效')
            redis_str_code = redis_code.decode('utf-8') ## 把获取到的字节数据变成字符串
            if code.strip() != redis_str_code.strip():
                raise ValidationError('输入的验证码错误')
        return code

class LoginForm(BootStrapForm,forms.Form):
    username = forms.CharField(
        label='用户名',
    )
    password = forms.CharField(
        label='密码',
        widget=forms.PasswordInput(render_value=True))  ## 利用插件将密码框改成密码类型的输入框，输入数据时将显示...或者***
    code = forms.CharField(
        label='验证码',
        widget=forms.TextInput()
    )
    def __init__(self,request,*args,**kwargs):
        super().__init__(*args,**kwargs)
        self.request = request
    # def clean_username(self):
    #     username = self.request.POST.get('username')
    #     password = self.request.POST.get('password')
    #     user_object = models.UserInfo.objects.filter(username=username).first()  ## 在数据库中查询获取的手机号是否存在记录，返回的结果为bool值
    #     if not user_object:
    #         raise ValidationError('用户不存在')
    #     elif password != user_object.password:
    #         raise ValidationError('用户名或者密码错误')
    #     return user_object
    def clean_code(self):
        """图片验证码"""

        code = self.cleaned_data.get('code')
        session_code = self.request.session.get('image_code')
        if not session_code:
            raise ValidationError('验证码已过期')
        elif session_code.strip().upper() != code.strip().upper():
            raise ValidationError('输入的验证码错误！')
        return code
    def clean_password(self):
        pwd = md5(self.cleaned_data.get('password'))
        return pwd







